/**
 * @author Dylan Jones, Ryan Hammonds
 */
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.ByteBuffer;
import java.util.HashSet;
import java.util.Properties;

import org.apache.commons.codec.binary.Hex;

public class RainbowTable {

	private Properties prop;
	private HashSet<String> plainText;

	public RainbowTable() {
		prop = new Properties();
		plainText = new HashSet<String>();
	}

	@SuppressWarnings("unchecked")
	public RainbowTable (File tableFile, File textFile) throws FileNotFoundException, IOException, ClassNotFoundException{
		ObjectInputStream oStream = new ObjectInputStream (new FileInputStream(textFile));
		plainText = (HashSet<String>) oStream.readObject();
		oStream.close();
		prop = new Properties();
		prop.load(new FileInputStream(tableFile));
	}
	
	public void addToTable(String key, String value){
		plainText.add(value);
		prop.setProperty(key, value);
	}

	public String searchTable(String key){
		return prop.getProperty(key);
	}

	public void save(File tableFile, File textFile) throws FileNotFoundException, IOException {
		ObjectOutputStream objectOut = new ObjectOutputStream(new FileOutputStream(textFile));
		objectOut.writeObject(plainText);
		objectOut.close();
		prop.store(new ObjectOutputStream(new FileOutputStream(tableFile)), null);
	}

	public boolean containsPlainText(String text) {
		return plainText.contains(text);
	}
	
	public String crackHash(String hash, int length, boolean[] charSets){
		String inTable = this.searchTable(hash);
		if(inTable != null){
			return inTable;
		}
		
		HashSet<Character> chars = new HashSet<Character>();
		if (charSets[0]){
			for(int i = 0; i < 26; i++){
				chars.add((char) (i+'A'));
			}
		}
	
		if (charSets[1]){
			for (int i =0; i < 26; i++){
				chars.add((char) (i+'a'));
			}
		}
		
		if (charSets[2]){
			for (int i=0; i < 10; i++){
				chars.add((char) (i+'0'));
			}
		}
		
		if (charSets[3]){
			for (int i=32; i<=47; i++){
				chars.add((char)i);
			}
			for (int i=58; i<=64; i++){
				chars.add((char)i);
			}
			for (int i=91; i<=96; i++){
				chars.add((char)i);
			}
			for (int i= 123; i<=126; i++){
				chars.add((char)i);
			}
		}
		
		StringGenerator generator = new StringGenerator(chars, length);
		String hashIn = hash.toLowerCase();
		StringBuilder hashOut;
		int[] hashBack;
		for(String out : generator){
			if(this.containsPlainText(out))
				continue;
			hashOut = new StringBuilder();
			hashBack = SHA1Hash.ShaHash(out);
			for(int i = 0; i < 5; i++)
				hashOut.append(Hex.encodeHexString(ByteBuffer.allocate(4).putInt(hashBack[i]).array()));
			this.addToTable(hashOut.toString(), out);
			if(hashIn.equals(hashOut.toString())){
				return out;
			}
		}
		return null;
	}
}
